/*
 * $Id: QEMode.m 100 2009-08-13 01:09:06Z jon@bway.net $
 * Chalk
 *
 * Created by Jon Gordon on 4/25/09
 * Copyright Jon Gordon.  All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining 
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including 
 * without limitation the rights to use, copy, modify, merge, publish, 
 * distribute, sublicense, and/or sell copies of the Software, and to 
 * permit persons to whom the Software is furnished to do so, subject to 
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#import "QEMode.h"

#import "chalk.h"

static NSMutableSet *allModes;
static NSMutableDictionary *allModesByUid;
static NSMutableDictionary *allModesByName;

static NSDictionary *modeCorrections;

@interface QEMode (private)
- (id)initWithUid:(NSNumber *)newUid name:(NSString *)newName fullModeName:(NSString *)newFullModeName;

+ (QEMode *)createModeWithUid:(uint32_t)uid name:(NSString *)name fullModeName:(NSString *)fullModeName;
+ (void)createAllModes;
@end


@implementation QEMode
+ (void)initialize
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    static BOOL initialized = NO;
    
    if (!initialized) {
        //-NSLog(@"  initializing");
        initialized = YES;
        
        allModes = [[NSMutableSet alloc] init];
        allModesByUid = [[NSMutableDictionary alloc] init];
        allModesByName = [[NSMutableDictionary alloc] init];
        
        [QEMode createAllModes];
        
        // set up the list of mode corrections--the key is a non-standard
        // mode designation, and the value is the correct mode name
        NSMutableDictionary *mc = [[NSMutableDictionary alloc] init];
        [mc setObject:@"SSB"    forKey:@"A3J"];
        [mc setObject:@"SSB"    forKey:@"J3E"];
        [mc setObject:@"SSB"    forKey:@"USB"];
        [mc setObject:@"SSB"    forKey:@"LSB"];
        [mc setObject:@"SSB"    forKey:@"PHONE"];
        [mc setObject:@"SSB"    forKey:@"VOICE"];
        [mc setObject:@"CW"     forKey:@"A1"];
        [mc setObject:@"ASCI"   forKey:@"ASCII"];
        [mc setObject:@"CLO"    forKey:@"CLOVER"];
        [mc setObject:@"HELL"   forKey:@"HELLSCHREIBER"];
        [mc setObject:@"JT44"   forKey:@"FSK441"];
        [mc setObject:@"PAC"    forKey:@"PACTOR"];
        [mc setObject:@"PAC2"   forKey:@"PACTOR II"];
        [mc setObject:@"PAC3"   forKey:@"PACTOR III"];
        [mc setObject:@"PKT"    forKey:@"PACKET"];
        [mc setObject:@"PSK31"  forKey:@"PSK"];
        [mc setObject:@"PSK31"  forKey:@"BPSK"];
        [mc setObject:@"PSK31"  forKey:@"BPSK31"];
        [mc setObject:@"PSK31"  forKey:@"QPSK"];
        [mc setObject:@"PSK63"  forKey:@"BPSK63"];
        [mc setObject:@"PSK125" forKey:@"PSK125F"];
        [mc setObject:@"Q15"    forKey:@"Q15X25"];
        [mc setObject:@"THRB"   forKey:@"THROB"];
        [mc setObject:@"TOR"    forKey:@"AMTOR"];
        modeCorrections = [[NSDictionary alloc] initWithDictionary:mc];
        [mc release];
    } // if
} // initialize

- (id)init
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    // guaranteed to fail assertions
    return [self initWithUid:nil name:nil fullModeName:nil];
} // init

- (id)initWithUid:(NSNumber *)newUid name:(NSString *)newName fullModeName:(NSString *)newFullModeName
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    NSAssert(nil != newUid, @"  data integrity check FAIL: nil mode UID");
    NSAssert(nil != newName, @"  data integrity check FAIL: nil mode name");
    NSAssert(0 != [newUid unsignedLongValue], @" data integrity check FAIL: zero mode UID");
    
    self = [super init];
    if (nil != self) {
        uid = [newUid retain];
        name = [[NSString alloc] initWithString:newName];
        fullModeName = [[NSString alloc] initWithString:newFullModeName];
    } // if
    
    return self;
} // initWithUid:name:fullModeName:


+ (NSArray *)allModes
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    return [allModes allObjects];
} // allModes


+ (BOOL)isValidModeName:(NSString **)namePtr
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    //-NSLog(@"  entry: mode name = \"%@\"", *namePtr);
    return [QEMode validateMode:namePtr error:nil];
    //-NSLog(@"   exit: mode name = \"%@\"", *namePtr);
} // isValidModeName

+ (BOOL)validateMode:(id *)ioValue error:(NSError **)outError
{
    
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    NSAssert(nil != ioValue, @"  validateMode passed nil pointer to mode string");

    if (nil == *ioValue) return YES;
    
    BOOL result = NO;
    
    NSString *uprName = [*ioValue uppercaseString];
    NSString *correctedMode = [modeCorrections objectForKey:uprName];
    //-NSLog(@"  input mode name: %@", *ioValue);
    //-NSLog(@"  uprName: %@; correctedMode: %@", uprName, correctedMode);
    if (nil == correctedMode) {
        correctedMode = uprName;
    } // if
    //-NSLog(@"  uprName: %@; correctedMode: %@", uprName, correctedMode);
    result = (nil != [QEMode getModeForName:correctedMode]);
    if (result && ![correctedMode isEqual:(*ioValue)]) {
        *ioValue = correctedMode;
    } else if (!result && (nil != outError)) {
        NSArray *keys = [NSArray arrayWithObjects:NSLocalizedDescriptionKey,
                         NSLocalizedRecoverySuggestionErrorKey, nil];
        NSArray *objects = [NSArray arrayWithObjects:
                            NSLocalizedString(@"Unrecognized mode", @""),
                            [NSString stringWithFormat:
                             NSLocalizedString(@"\"%@\" does not name any known mode.", @""), *ioValue],
                            nil];
        *outError = [[[NSError alloc] initWithDomain:QEChalkErrorDomain
                                                code:QEModeUnknownNameError 
                                            userInfo:[NSDictionary dictionaryWithObjects:objects forKeys:keys]] autorelease];
    } // if
    
    return result;
} // validateMode:error:

+ (QEMode *)getModeForName:(NSString *)name
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    if (nil == name) {
        return nil;
    } // if
    
    return [allModesByName objectForKey:name];
} // getModeForName:

+ (QEMode *)getModeForUid:(NSNumber *)uid
{
    //-NSLog(@"MESSAGE: %s", __PRETTY_FUNCTION__);
    if (nil == uid) {
        return nil;
    } // if
    
    return [allModesByUid objectForKey:uid];
} // getModeForUid:


- (NSNumber *)uid
{
    return [[uid retain] autorelease];
} // uid

- (NSString *)name
{
    return [[name retain] autorelease];
} // name

- (NSString *)fullModeName
{
    return [[fullModeName retain] autorelease];
} // fullModeName


+ (QEMode *)createModeWithUid:(uint32_t)uid name:(NSString *)name fullModeName:(NSString *)fullModeName
{
    NSNumber *uidObj = [NSNumber numberWithUnsignedLong:uid];
    NSAssert2(nil == [allModesByUid objectForKey:uidObj], 
              @"  data integrity check FAIL: uid %@ already used for mode \"%@\"",
              uidObj, [[allModesByUid objectForKey:uidObj] name]);
    NSAssert2(nil == [allModesByName objectForKey:name], 
              @"  data integrity check FAIL: name \"%@\" already used for mode with UID %@",
              name, [[allModesByName objectForKey:name] uid]);
    
    QEMode *newMode = [[QEMode alloc] initWithUid:uidObj name:name fullModeName:fullModeName];
    if (nil != newMode) {
        [allModes addObject:newMode];
        [allModesByUid setObject:newMode forKey:uidObj];
        [allModesByName setObject:newMode forKey:name];
        [newMode release]; // retained by collections
    } // if
    
    return newMode;
} // createModeWithUid:name:fullModeName:

+ (void)createAllModes
{
    [QEMode createModeWithUid:1  name:@"AM" fullModeName:@"amplitude modulation"];
    [QEMode createModeWithUid:2  name:@"AMTORFEC" fullModeName:@"amateur teleprinting over radio with forward error correction"];
    [QEMode createModeWithUid:3  name:@"ASCI" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:4  name:@"ATV" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:5  name:@"CHIP64" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:6  name:@"CHIP128" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:7  name:@"CLO" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:8  name:@"CONTESTI" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:9  name:@"CW" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:10 name:@"DSTAR" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:11 name:@"DOMINO" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:12 name:@"DOMINOF" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:13 name:@"FAX" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:14 name:@"FM" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:15 name:@"FMHELL" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:16 name:@"FSK31" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:17 name:@"FSK441" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:18 name:@"GTOR" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:19 name:@"HELL" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:20 name:@"HELL80" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:21 name:@"HFSK" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:22 name:@"JT44" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:23 name:@"JT65" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:24 name:@"JT65A" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:25 name:@"JT6M" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:26 name:@"MFSK8" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:27 name:@"MFSK16" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:28 name:@"MT63" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:29 name:@"OLIVIA" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:30 name:@"PAC" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:31 name:@"PAC2" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:32 name:@"PAC3" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:33 name:@"PAX" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:34 name:@"PAX2" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:35 name:@"PCW" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:36 name:@"PKT" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:37 name:@"PSK10" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:38 name:@"PSK31" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:39 name:@"PSK63" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:40 name:@"PSK63F" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:41 name:@"PSK125" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:42 name:@"PSKAM10" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:43 name:@"PSKAM31" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:44 name:@"PSKAM50" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:45 name:@"PSKFEC31" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:46 name:@"PSKHELL" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:47 name:@"QPSK31" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:48 name:@"QPSK63" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:49 name:@"QPSK125" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:50 name:@"RTTY" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:51 name:@"RTTYM" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:52 name:@"SSB" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:53 name:@"SSTV" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:54 name:@"THRB" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:55 name:@"THOR" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:56 name:@"THRBX" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:57 name:@"TOR" fullModeName:@"XXXX"];
    [QEMode createModeWithUid:58 name:@"VOI" fullModeName:@"XXXX"];
} // createAllModes

@end
